package com.unknow.first.dapp.admin.account.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lianziyou.coin.price.util.CoinPriceUtil;
import com.unknow.first.dapp.admin.account.constants.AccountConstants;
import com.unknow.first.dapp.admin.account.constants.AccountConstants.TradeStatus;
import com.unknow.first.dapp.admin.account.constants.ITradeTypeEnum;
import com.unknow.first.dapp.admin.account.exception.AddressAccountException;
import com.unknow.first.dapp.admin.account.mapper.AddressAccountDetail;
import com.unknow.first.dapp.admin.account.mapper.AddressAccountInfo;
import com.unknow.first.dapp.admin.account.mapper.AddressAccountInfoMapper;
import com.unknow.first.dapp.admin.account.mapper.AddressCoinConfig;
import com.unknow.first.dapp.admin.account.service.AddressAccountDetailService;
import com.unknow.first.dapp.admin.account.service.AddressAccountInfoService;
import com.unknow.first.dapp.admin.account.service.AddressCoinConfigService;
import com.unknow.first.dapp.admin.account.utils.AddressAccountUtil;
import org.cloud.core.redis.RedisUtil;
import org.cloud.utils.ExceptionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import static com.unknow.first.dapp.admin.account.constants.AccountErrorConstants.ACCOUNT_DETAIL_INSERT_ERROR;

/**
 * @author Administrator
 * @description 针对表【t_address_account_info】的数据库操作Service实现
 * @createDate 2022-05-17 10:21:36
 */
@Service
public class AddressAccountInfoServiceImpl extends ServiceImpl<AddressAccountInfoMapper, AddressAccountInfo> implements AddressAccountInfoService {


    @Autowired
    RedisUtil redisUtil;
    @Autowired
    private AddressCoinConfigService addressCoinConfigService;

    @Lazy
    @Autowired
    private AddressAccountDetailService addressAccountDetailService;

    public final static String CREATE_ADDRESS_AMOUNT_LOCKER = "system:addressAccount:locker:createAddressAccount:";

    @Override
    public int createAddressAccount(AddressAccountInfo accountInfo) throws Exception {
        // 创建的时候加锁，防止重复创建
        final String locker = CREATE_ADDRESS_AMOUNT_LOCKER + accountInfo.getAddress() + "." + accountInfo.getCoinId();
        if (!redisUtil.getLock(locker, 60 * 60 * 1000)) {
            throw new AddressAccountException("addressAccount.error.otherSessionIsCreateAddressAccount");
        }
        try {
            return this.getBaseMapper().insert(accountInfo);
        } catch (Exception e) {
            throw new AddressAccountException("addressAccount.error.otherSessionIsCreateAddressAccount", e, HttpStatus.BAD_REQUEST.value());
        } finally {
            redisUtil.releaseLock(locker);
        }
    }

    @Override
    public List<AddressAccountInfo> queryAccountInfoByAddress(Map<String, Object> params) throws Exception {

        AddressAccountUtil.single().getAccountInfo(params.get("address").toString());

        List<AddressAccountInfo> addressAccountInfoList = this.getBaseMapper().queryAccountInfoByAddress(params);

        addressAccountInfoList.forEach(addressAccountInfo -> {
            BigDecimal price = CoinPriceUtil.single().getUsdtPrice(addressAccountInfo.getCoinName());
            if (price != null) {
                addressAccountInfo.setUsdtValue(price.multiply(addressAccountInfo.getBalance()));
            }
        });

        return addressAccountInfoList;
    }

    @Autowired
    DataSourceTransactionManager transactionManager;

    @Override
    public boolean updateBalanceByAccountDetail(AddressAccountDetail accountDetail) throws Exception {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            int updateBalanceByAccountDetail = getBaseMapper().updateBalanceByAccountDetail(accountDetail);
            Assert.isTrue(updateBalanceByAccountDetail > 0, "updateBalanceByAccountDetail fail");
            transactionManager.commit(status);
            return true;
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw new AddressAccountException(ExceptionUtil.single().getFinalCause(e).getMessage(), e, HttpStatus.BAD_REQUEST.value());
        }
    }

    @Override
    public AddressAccountDetail processFundAndDetail(BigDecimal amount, AddressCoinConfig coinConfig, ITradeTypeEnum tradeType, String remark, Long referId,
        String address, String admin) throws Exception {
        return this.processFundAndDetail(amount, coinConfig, tradeType, remark, referId, address, admin, TradeStatus.DONE.value);
    }

    @Override
    public AddressAccountDetail processFundAndDetail(BigDecimal amount, AddressCoinConfig coinConfig, ITradeTypeEnum tradeType, String remark, Long referId,
        String address, String admin, int status) throws Exception {
        AddressAccountDetail accountDetail = new AddressAccountDetail();
        accountDetail.setAmount(amount);
        accountDetail.setCoinId(coinConfig.getCoinId());
        accountDetail.setCoinName(coinConfig.getCoinName());
        accountDetail.setCoinSymbol(coinConfig.getCoinSymbol());
        accountDetail.setTradeType(tradeType.getValue());
        accountDetail.setTradeTypeI18n(tradeType.getI18nValue());
        accountDetail.setStatus(status);
        accountDetail.setRemark(remark);
        accountDetail.setReferId(referId);
        accountDetail.setAddress(address);
        accountDetail.setCreatedBy(admin);
        accountDetail.setUpdatedBy(admin);
        accountDetail.setIsShow(tradeType.isShow() ? AccountConstants.AccountDetailIsShow.SHOW.value : AccountConstants.AccountDetailIsShow.HIDE.value);
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY);
        TransactionStatus _status = transactionManager.getTransaction(def);
        try {
            int insertDetail = addressAccountDetailService.getBaseMapper().insert(accountDetail);
            Assert.isTrue(insertDetail > 0, ACCOUNT_DETAIL_INSERT_ERROR.code);
            // 状态是完成或者是待确认扣除账户的资金余额
            if (status == TradeStatus.DONE.value || status == TradeStatus.UNCONFIRMED.value) {
                this.updateBalanceByAccountDetail(accountDetail);
            }
            transactionManager.commit(_status);
            return accountDetail;
        } catch (Exception e) {
            transactionManager.rollback(_status);
            throw new AddressAccountException(ExceptionUtil.single().getFinalCause(e).getMessage(), e, HttpStatus.BAD_REQUEST.value());
        }
    }

    @Override
    public void createAddressAccount(String address) {

        if (!StringUtils.hasLength(address)) {
            return;
        }

        List<AddressCoinConfig> coinConfigs = addressCoinConfigService.getList(true);
        for (AddressCoinConfig coinConfig : coinConfigs) {
            AddressAccountDetail accountDetail = new AddressAccountDetail();
            accountDetail.setAddress(address);
            accountDetail.setCoinId(coinConfig.getCoinId());
            accountDetail.setCoinSymbol(coinConfig.getCoinSymbol());
            accountDetail.setCoinName(coinConfig.getCoinName());
            accountDetail.setAmount(BigDecimal.ZERO);
            accountDetail.setCreatedBy("System");
            accountDetail.setUpdatedBy("System");
            Assert.isTrue(getBaseMapper().updateBalanceByAccountDetail(accountDetail) > 0, "创建账户失败");
        }
    }

    @Override
    public AddressAccountInfo getAddressAccountByCoinId(String address, Integer coinId) {
        QueryWrapper<AddressAccountInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AddressAccountInfo::getAddress, address).eq(AddressAccountInfo::getCoinId, coinId);
        AddressAccountInfo addressAccount = getBaseMapper().selectOne(queryWrapper);
        if (addressAccount == null) {
            this.createAddressAccount(address);
            addressAccount = getBaseMapper().selectOne(queryWrapper);
        }
        return addressAccount;
    }

    @Override
    public int updateStatusById(Long id, Integer status) {
        return getBaseMapper().updateStatusById(id, status);
    }
}


